#include "vcl_stl.h"

#include "bkbase.h"
#include "bkipc.h"
#include "bstcp.h"

#include <sys/timeb.h>
#include <sys/wait.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "h_base.h"
#include "h_xml.h"
#include "h_lz77.h"
using namespace h_base;

#include "simbat.h"
using namespace simbat;

#define PASS_KEY "sim bat 1.0.0   "

static bool TermFlag = false;
static bool RefreshFlag = false;
static int TermStep = 0;
static int RunningPgmQty = 0;
static char *SimbatBase = NULL;
static bool Copyright = false;
static char ErrMsg[8192 + 1];
static vector<int> SockExecVec;
static int SockSch = -1;
static int SockDb = -1;
static int LockFd = -1;
static int NumOfExecutor = 0;
static int Backlog = 0;              // config.xml 里面配置BACKLOG参数，缺省值是100，建议配置成总并发数
                                     // 操作系统TCP参数需要调整
                                     // /etc/sysctl.conf
                                     // net.core.somaxconn=16384 这个是系统TCP连接队列深度，不小于总并发数
                                     // net.ipv4.tcp_tw_reuse=1
                                     // net.ipv4.tcp_tw_recycle=1 这两个参数都与TIME_WAIT状态快速消除有关
                                     // sysctl -p 生效

static int MyId = -1;                // 本服务器ID（从0开始）
static HNodeRole MyRole = nrMaster;  // 本服务器角色（MASTER/SLAVE/BACKUP）
static int NumOfDbProcess = 30;
static HString JobNameDelimiter = ":";

struct HPgmInfo
{
   HString Name;
   int Idx;

   HPgmInfo()
      : Idx(0)
   {}
};

static map<pid_t, HPgmInfo> PgmMap;

struct HClusterNodeInfo
{
   HString Addr;
   int Port;

   HClusterNodeInfo()
      : Port(-1)
   {}
};

static vector<HClusterNodeInfo> ClusterNodeVec;     // 集群配置信息

static void HandleSignalTerm(int)
{
   TermFlag = true;
}

static void HandleSignalHup(int)
{
   RefreshFlag = true;
}

static HString SyslogDir;
static HSyslog Syslog;
static int LocalPort;
static HString ShrDir;
static HString LogDir;
static HString SelfAddr;

static void LoadFromCfg(HXmlTree &Cfg)
{
   char ErrMsg[8192 + 1];

   SyslogDir = EnvEvaluate(Cfg.GetValue("SYSLOG_DIR", "$SIMBAT_BASE/etc"));
   Syslog.Setup(SyslogDir, Cfg.GetValueUpper("SYSLOG_LEVEL", "INFO"));

   if ( !GetMyId(SimbatBase, MyId, MyRole, ErrMsg) )
   {
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      return;
   }

   LocalPort = StrToInt(Cfg.GetValue("PORT").c_str());
   ShrDir = EnvEvaluate(Cfg.GetValue("SHR_DIR", "$SIMBAT_BASE/shr"));
   LogDir = EnvEvaluate(Cfg.GetValue("LOG_DIR", "$SIMBAT_BASE/log"));
   setenv("SIMBAT_SHR_DIR", ShrDir.c_str(), 1);
   setenv("SIMBAT_LOG_DIR", LogDir.c_str(), 1);
   NumOfExecutor = StrToInt(Cfg.GetValue("NUM_OF_EXECUTOR", "3").c_str());
   NumOfDbProcess = StrToInt(Cfg.GetValue("NUM_OF_DB_PROCESS", "30").c_str());
   Backlog = StrToInt(Cfg.GetValue("BACKLOG", "100").c_str());
   JobNameDelimiter = Cfg.GetValue("JOB_NAME_DELIMITER", ":");

   if ( NumOfExecutor < 3 )
   {
      NumOfExecutor = 3;
   }

   if ( NumOfDbProcess < 3 )
   {
      NumOfDbProcess = 3;
   }

   char TmpStr[500 + 1];

   snprintf(TmpStr, 500, "%s:%d", "127.0.0.1", LocalPort);
   SelfAddr = TmpStr;
}

static bool LoadClusterInfo(HXmlTree &Cfg, HString &ErrStr)
{
   vector<HString> StrVec;
   char ErrMsg[8192 + 1];

   Cfg.GetValues("CLUSTER", StrVec);
   ClusterNodeVec.clear();

   if ( StrVec.empty() )
   {
      HClusterNodeInfo Node;
      Node.Addr = "127.0.0.1";
      Node.Port = LocalPort;
      ClusterNodeVec.push_back(Node);
      return true;
   }

   for ( int i = 0; i < StrVec.size(); i++ )
   {
      HString &AddrStr = StrVec[i];
      vector<HString> AddrPort;
      SplitString(AddrStr.c_str(), ':', AddrPort, true);
      if ( AddrPort.size() != 2 && AddrPort.size() != 3 )
      {
         snprintf(ErrMsg, 8192, "Invalid cluster address(%s)", AddrStr.c_str());
         ErrStr = ErrMsg;
         return false;
      }
      HClusterNodeInfo Node;
      Node.Addr = AddrPort[0];
      Node.Port = StrToInt(AddrPort[1].c_str());
      if ( Node.Port <= 2048 )
      {
         snprintf(ErrMsg, 8192, "Port %s too small, must be greater than 2048", AddrStr.c_str());
         ErrStr = ErrMsg;
         return false;
      }
      ClusterNodeVec.push_back(Node);
   }

   return true;
}

static bool LoadCfg(HString &ErrStr)
{
   char FileName[500 + 1];
   HXmlTree Cfg;
   HFileLockRead Lock("$SIMBAT_BASE/tmp/config.lck");

   snprintf(FileName, 500, "%s/etc/config.xml", SimbatBase);

   if ( !Cfg.LoadFromFile(FileName) )
   {
      ErrStr = Cfg.GetErrorString();
      return false;
   }

   LoadFromCfg(Cfg);

   if ( !LoadClusterInfo(Cfg, ErrStr) )
   {
      return false;
   }

   return true;
}

static void StartPgm(const HString &PgmName, int PgmIdx = 0, bool Sleep = false)
{
   for ( ; ; )
   {
      pid_t pid = fork();
      if ( pid < 0 )
      {
         snprintf(ErrMsg, 8192, "fork fail, errno = %d", errno);
         Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
         sleep(30);
         continue;
      }
      if ( pid == 0 )
      {
         BkSignal(SIGTERM, SIG_DFL, 0);
         if ( Sleep )
         {
            sleep(10);
         }
         close(LockFd);
         char SockFdStr[80 + 1];
         if ( PgmName != "simbat_sch" )
         {
            close(SockSch);
         }
         if ( PgmName == "simbat_exec" )
         {
            for ( int i = 0; i < NumOfExecutor; i++ )
            {
               if ( i != PgmIdx )
               {
                  close(SockExecVec[i]);
               }
            }
         }
         if ( PgmName != "simbat_exec" )
         {
            for ( int i = 0; i < NumOfExecutor; i++ )
            {
               close(SockExecVec[i]);
            }
         }
         if ( PgmName != "simbat_db" )
         {
            close(SockDb);
         }
         SockFdStr[0] = '\0';
         if ( PgmName == "simbat_sch" )
         {
            snprintf(SockFdStr, 80, "%d", SockSch);
         }
         if ( PgmName == "simbat_exec" )
         {
            snprintf(SockFdStr, 80, "%d", SockExecVec[PgmIdx]);
         }
         if ( PgmName == "simbat_db" )
         {
            snprintf(SockFdStr, 80, "%d", SockDb);
         }
         execlp(PgmName.c_str(), PgmName.c_str(), SockFdStr, NULL);
         snprintf(ErrMsg, 8192, "execlp(%s) fail, errno = %d", PgmName.c_str(), errno);
         Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
         exit(8);
      }
      HPgmInfo Pgm;
      Pgm.Name = PgmName;
      Pgm.Idx = PgmIdx;
      PgmMap[pid] = Pgm;
      snprintf(ErrMsg, 8192, "program(%s) started, pid = %d", PgmName.c_str(), pid);
      Syslog.Log(__FILE__, __LINE__, llInfo, "OTHER", ErrMsg);
      RunningPgmQty++;
      break;
   }
}

static void DoRefresh()
{
   HString ErrStr;

   Syslog.Log(__FILE__, __LINE__, llInfo, "OTHER", "Reload config.xml");

   if ( !LoadCfg(ErrStr) )
   {
      snprintf(ErrMsg, 8192, "Load config.xml fail, %s", ErrStr.c_str());
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
   }
}

static void StartSimbat()
{
   int ConsoleFd;
   char TmpStr[500 + 1];
   char FileName[500 + 1];

// 处理标准输入、标准输出、标准错误
   for ( int i = 0; i < 1000; i++ )
   {
      close(i);
   }

   open("/dev/null", O_RDONLY);
   dup2(0, 1);
   dup2(0, 2);

   {
      char Command[2048 + 1];
      struct timeb Time;
      struct tm *tm;
      ftime(&Time);
      tm = localtime(&(Time.time));
      snprintf(Command, 2048, "cp %s/etc/console.msg "
         "%s/etc/console.msg.%04d%02d%02d_%02d%02d%02d 2>/dev/null",
         SimbatBase, SimbatBase,
         tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
         tm->tm_hour, tm->tm_min, tm->tm_sec);
      system(Command);
   }

   close(1);
   close(2);
   snprintf(FileName, 500, "%s/etc/console.msg", SimbatBase);
   unlink(FileName);
   ConsoleFd = open(FileName, O_CREAT | O_WRONLY | O_APPEND, 0666);

   if ( ConsoleFd < 0 )
   {
      ConsoleFd = open("/dev/null", O_WRONLY);
   }

   dup2(1, 2);

   SockSch = BsCreateServerSocket(NULL, LocalPort, Backlog);

   if ( SockSch < 0 )
   {
      snprintf(ErrMsg, 8192, "Create socket fail, errno = %d", errno);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      return;
   }

   SockExecVec.clear();

   if ( MyRole == nrMaster || MyRole == nrSlave )
   {
      for ( int i = 0; i < NumOfExecutor; i++ )
      {
         snprintf(FileName, 500, "%s/tmp/socket_exec%d", SimbatBase, i);
         int SockExec = BsCreateServerSocketUD(FileName, NumOfExecutor);
         if ( SockExec < 0 )
         {
            snprintf(ErrMsg, 8192, "BsCreateServerSocketUD '%s' fail, r = %d, errno = %d",
               FileName, SockExec, errno);
            Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
            return;
         }
         SockExecVec.push_back(SockExec);
      }
   }

   NumOfExecutor = SockExecVec.size();

   if ( MyRole == nrMaster || MyRole == nrGateway )
   {
      snprintf(FileName, 500, "%s/tmp/socket_db", SimbatBase);
      SockDb = BsCreateServerSocketUD(FileName, 5);
      if ( SockDb < 0 )
      {
         snprintf(ErrMsg, 8192, "BsCreateServerSocketUD '%s' fail, r = %d, errno = %d",
            FileName, SockDb, errno);
         Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
         return;
      }
   }

// 脱离控制终端
   setsid();

// 更改工作目录
   snprintf(FileName, 500, "%s/tmp", SimbatBase);
   chdir(FileName);

// 锁文件，防止重复启动
   snprintf(FileName, 500, "%s/tmp/simbat.lck", SimbatBase);
   unlink(FileName);

   LockFd = BkOpenLockFile(FileName);

   snprintf(TmpStr, 500, "%010d\n", getpid());
   write(LockFd, TmpStr, strlen(TmpStr));
   BkWriteLock(LockFd, 0, 0, 0);

// 设置退出信号处理
   BkSignal(SIGTERM, HandleSignalTerm, 1);
   BkSignal(SIGHUP, HandleSignalHup, 1);
   StartPgm("simbat_sch");
   StartPgm("simbat_clean");

   if ( MyRole == nrMaster || MyRole == nrSlave )
   {
      for ( int i = 0; i < NumOfExecutor; i++ )
      {
         StartPgm("simbat_exec", i);
      }
   }

   if ( MyRole == nrMaster )
   {
      StartPgm("simbat_db");
   }

   if ( MyRole == nrGateway )
   {
      for ( int i = 0; i < NumOfDbProcess; i++ )
      {
         StartPgm("simbat_db");
      }
   }

   snprintf(ErrMsg, 8192, "SIM-BAT started, pid = %d", getpid());
   Syslog.Log(__FILE__, __LINE__, llInfo, "OTHER", ErrMsg);
   BkPrintError("%s", ErrMsg);

   for ( ; ; )
   {
      int Stat;
      pid_t pid = waitpid(-1, &Stat, 0);
      if ( pid < 0 )
      {
         if ( errno != EINTR )
         {
            snprintf(ErrMsg, 8192, "waitpid fail, errno = %d", errno);
            Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
            sleep(60);
            continue;
         }
         if ( TermStep == 0 && TermFlag )
         {
            TermStep = 1;
            snprintf(ErrMsg, 8192, "SIM-BAT is stopping...");
            Syslog.Log(__FILE__, __LINE__, llInfo, "OTHER", ErrMsg);
            for ( map<pid_t, HPgmInfo>::iterator Iter = PgmMap.begin();
                  Iter != PgmMap.end(); Iter++ )
            {
               snprintf(ErrMsg, 8192, "killing process %s(%d) ...",
                  Iter->second.Name.c_str(), Iter->first);
               Syslog.Log(__FILE__, __LINE__, llInfo, "OTHER", ErrMsg);
               kill(Iter->first, SIGTERM);
            }
            continue;
         }
         if ( RefreshFlag )
         {
            DoRefresh();
            RefreshFlag = false;
         }
         continue;
      }
      RunningPgmQty--;
      if ( TermStep > 0 )
      {
         PgmMap.erase(pid);
      }
      else
      {
         HPgmInfo Pgm = PgmMap[pid];
         if ( WIFEXITED(Stat) )
         {
            if ( WEXITSTATUS(Stat) != 0 )
            {
               snprintf(ErrMsg, 8192, "program(%s, %d) return %d",
                  Pgm.Name.c_str(), pid, WEXITSTATUS(Stat));
               Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
               PgmMap.erase(pid);
               StartPgm(Pgm.Name, Pgm.Idx, true);
            }
            else
            {
               PgmMap.erase(pid);
               StartPgm(Pgm.Name, Pgm.Idx, false);
            }
         }
         else if ( WIFSIGNALED(Stat) )
         {
            snprintf(ErrMsg, 8192, "program(%s, %d) killed by signal %d",
               Pgm.Name.c_str(), pid, WTERMSIG(Stat));
            Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
            PgmMap.erase(pid);
            StartPgm(Pgm.Name, Pgm.Idx, true);
         }
         else
         {
            snprintf(ErrMsg, 8192, "program(%s, %d) down, stat = %08x",
               Pgm.Name.c_str(), pid, Stat);
            Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
            PgmMap.erase(pid);
            StartPgm(Pgm.Name, Pgm.Idx, true);
         }
      }
      if ( RunningPgmQty == 0 )
      {
         break;
      }
   }

   close(LockFd);
   close(ConsoleFd);
}

static int Start()
{
   snprintf(ErrMsg, 8192, "Start()");
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   char FileName[500 + 1];
   BK_FS_INFO FsInfo;

   snprintf(FileName, 500, "%s/tmp", SimbatBase);
   BkGetFsInfo(FileName, &FsInfo);

   if ( FsInfo.free_blocks < 10 )  /* $SIMBAT_BASE/tmp 文件系统至少要剩余 10M 空间 */
   {
      snprintf(ErrMsg, 8192, "File system '%s' space insufficient!!!", FileName);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   snprintf(FileName, 500, "%s", ShrDir.c_str());
   BkGetFsInfo(FileName, &FsInfo);

   if ( FsInfo.free_blocks < 50 )  /* $SIMBAT_SHR_DIR 文件系统至少要剩余 50M 空间 */
   {
      snprintf(ErrMsg, 8192, "File system '%s' space insufficient!!!", FileName);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   snprintf(FileName, 500, "%s", LogDir.c_str());
   BkGetFsInfo(FileName, &FsInfo);

   if ( FsInfo.free_blocks < 50 )  /* $SIMBAT_LOG_DIR 文件系统至少要剩余 50M 空间 */
   {
      snprintf(ErrMsg, 8192, "File system '%s' space insufficient!!!", FileName);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   {
      int MyId;
      HNodeRole Role;
      if ( !GetMyId(SimbatBase, MyId, Role, ErrMsg) )
      {
         Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
         BkPrintError("%s", ErrMsg);
         return 8;
      }
      if ( MyId < 0 || MyId >= ClusterNodeVec.size() )
      {
         snprintf(ErrMsg, 8192, "Invalid my_id '%d'", MyId + 1);
         Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
         BkPrintError("%s", ErrMsg);
         return 8;
      }
#ifdef BK_LINUX
      if ( Role != nrBackup )
      {
         vector<HString> IpAddrVec;
         GetIpAddrVec(IpAddrVec);
         bool Found = false;
         HString IpAddr = ClusterNodeVec[MyId].Addr;
         for ( int i = 0; i < IpAddrVec.size(); i++ )
         {
            if ( IpAddrVec[i] == IpAddr )
            {
               Found = true;
               break;
            }
         }
         if ( !Found )
         {
            snprintf(ErrMsg, 8192, "Invalid IP addr '%s', my_id '%d'", IpAddr.c_str(), MyId + 1);
            Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
            BkPrintError("%s", ErrMsg);
            return 8;
         }
      }
#endif
   }

   int LockFd;

   snprintf(FileName, 500, "%s/tmp/simbat.lck", SimbatBase);
   LockFd = BkOpenLockFile(FileName);

   if ( LockFd < 0 )
   {
      snprintf(ErrMsg, 8192, "Can not create lock file %s, errno = %d", FileName, errno);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   if ( BkWriteLock(LockFd, 0, 0, 0) < 0 )
   {
      snprintf(ErrMsg, 8192, "System already started");
      Syslog.Log(__FILE__, __LINE__, llWarn, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   close(LockFd);

/* 检查通讯端口是否可用 */
   SockSch = BsCreateServerSocket(NULL, LocalPort, 5);

   if ( SockSch < 0 )
   {
      snprintf(ErrMsg, 8192, "Create socket fail, errno = %d", errno);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   close(SockSch);
   ForceMkdir(LogDir);
   ForceMkdir(ShrDir);
   ForceMkdir(ShrDir + "/cond");
   ForceMkdir(ShrDir + "/etc");
   ForceMkdir(ShrDir + "/events");
   ForceMkdir(ShrDir + "/his");
   ForceMkdir(ShrDir + "/part");
   ForceMkdir(ShrDir + "/part_bak");
   ForceMkdir(ShrDir + "/status");

   snprintf(FileName, 500, "%s/etc/config_cust.xml", ShrDir.c_str());

   if ( MyRole == nrMaster && !FileExist(FileName) )
   {
      char Command[2048 + 1];
      snprintf(Command, 2048, "cp $SIMBAT_BASE/etc/config_cust_default.xml %s", FileName);
      system(Command);
      snprintf(ErrMsg, 8192, "Create (%s)", FileName);
      Syslog.Log(__FILE__, __LINE__, llInfo, "OTHER", ErrMsg);
   }

   snprintf(FileName, 500, "%s/etc/auth.xml", ShrDir.c_str());

   if ( ( MyRole == nrMaster || MyRole == nrGateway ) && !FileExist(FileName) )
   {
      char Command[2048 + 1];
      snprintf(Command, 2048, "cp $SIMBAT_BASE/etc/auth_default.xml %s", FileName);
      system(Command);
      snprintf(ErrMsg, 8192, "Create (%s)", FileName);
      Syslog.Log(__FILE__, __LINE__, llInfo, "OTHER", ErrMsg);
   }

   pid_t pid = fork();

   if ( pid < 0 )
   {
      snprintf(ErrMsg, 8192, "fork fail, errno = %d", errno);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   if ( pid == 0 )
   {
      StartSimbat();
      snprintf(ErrMsg, 8192, "SIM-BAT stopped");
      Syslog.Log(__FILE__, __LINE__, llInfo, "OTHER", ErrMsg);
      BkPrintError("%s", ErrMsg);
      exit(0);
   }

   printf("SIM-BAT start successfully\n");
   return 0;
}

static int Stop()
{
   snprintf(ErrMsg, 8192, "Stop()");
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   int LockFd;
   char TmpStr[500 + 1];
   char FileName[500 + 1];

   snprintf(FileName, 500, "%s/tmp/simbat.lck", SimbatBase);
   LockFd = BkOpenLockFile(FileName);

   if ( LockFd < 0 )
   {
      snprintf(ErrMsg, 8192, "Can not create lock file %s, errno = %d", FileName, errno);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   if ( BkWriteLock(LockFd, 0, 0, 0) == 0 )
   {
      snprintf(ErrMsg, 8192, "System not start yet");
      Syslog.Log(__FILE__, __LINE__, llWarn, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   memset(TmpStr, 0, 20);
   read(LockFd, TmpStr, 20);
   TmpStr[20] = '\0';
   close(LockFd);
   kill(StrToInt(TmpStr), SIGTERM);
   return 0;
}

static int Status()
{
   snprintf(ErrMsg, 8192, "Status()");
   Syslog.Log(__FILE__, __LINE__, llDebug, "ENTRY", ErrMsg);

   int LockFd;
   char TmpStr[500 + 1];
   char FileName[500 + 1];

   snprintf(FileName, 500, "%s/tmp/simbat.lck", SimbatBase);
   LockFd = BkOpenLockFile(FileName);

   if ( LockFd < 0 )
   {
      snprintf(ErrMsg, 8192, "Can not create lock file %s, errno = %d", FileName, errno);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   if ( BkWriteLock(LockFd, 0, 0, 0) == 0 )
   {
      printf("System not start yet\n");
      close(LockFd);
      return 0;
   }
   else
   {
      read(LockFd, TmpStr, 10);
      TmpStr[10] = '\0';
      printf("System started, pid = %d\n", StrToInt(TmpStr));
   }

   close(LockFd);
   return 0;
}

static int Dump()
{
   snprintf(ErrMsg, 8192, "Dump()");
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   int r;
   HString Result;
   char Buf[500 + 1];
   snprintf(Buf, 500, "L||");

   if ( CallServer(Syslog, 60000, SelfAddr, "DUMP", "", Buf, true, Result) )
   {
      return 0;
   }
   else
   {
      BkPrintError("Dump fail, %s", Result.c_str());
      return 8;
   }
}

static int ReloadConfig()
{
   snprintf(ErrMsg, 8192, "ReloadConfig()");
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   int LockFd;
   char LockFileName[500 + 1];

   snprintf(LockFileName, 500, "%s/tmp/simbat.lck", SimbatBase);
   LockFd = BkOpenLockFile(LockFileName);

   if ( LockFd < 0 )
   {
      snprintf(ErrMsg, 8192, "Can not create lock file %s, errno = %d", LockFileName, errno);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   if ( BkWriteLock(LockFd, 0, 0, 0) == 0 )
   {
      snprintf(ErrMsg, 8192, "System not start yet");
      Syslog.Log(__FILE__, __LINE__, llWarn, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   char TmpStr[500 + 1];
   memset(TmpStr, 0, 20);
   read(LockFd, TmpStr, 20);
   TmpStr[20] = '\0';
   close(LockFd);
   kill(StrToInt(TmpStr), SIGHUP);

   int r;
   HString Result;
   char Buf[500 + 1];

   snprintf(Buf, 500, "N||");

   if ( CallServer(Syslog, 60000, SelfAddr, "RELOAD_CONFIG", "", Buf, true, Result) )
   {  
      return 0;
   }
   else
   {
      BkPrintError("Reload config fail, %s", Result.c_str());
      return 8;
   }
}

static int SwitchTo(const char *Role)
{
   snprintf(ErrMsg, 8192, "SwitchTo(%s)", Role);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   if ( strcmp(Role, "master") != 0 && strcmp(Role, "backup") != 0 )
   {
      snprintf(ErrMsg, 8192, "Invalid role '%s'", Role);
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   if ( strcmp(Role, "master") == 0 &&
        ( MyId != 0 || MyRole != nrBackup ) )
   {
      snprintf(ErrMsg, 8192, "This node is not backup");
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   if ( strcmp(Role, "backup") == 0 &&
        ( MyId != 0 || MyRole != nrMaster ) )
   {
      snprintf(ErrMsg, 8192, "This node is not master");
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   BkPrintError("Stopping SIM-BAT instance...");
   system("$SIMBAT_BASE/bin/simbat_ctl stop");
   BkPrintError("Change role to '%s'...", Role);

   char Command[500 + 1];
   snprintf(Command, 500, "echo '1 %s' > $SIMBAT_BASE/etc/my_id", Role);
   system(Command);
   BkPrintError("Waiting for 10 seconds...");
   sleep(10);
   BkPrintError("Starting SIM-BAT instance...");
   system("$SIMBAT_BASE/bin/simbat_ctl start");
   return 0;
}

static int EnableBackup(const char *BackupName)
{
   snprintf(ErrMsg, 8192, "EnableBackup(%s)", BackupName == NULL ? "" : BackupName);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   {
      char FileName[500 + 1];
      HXmlTree Cfg;
      HFileLockWrite Lock("$SIMBAT_BASE/tmp/config.lck");
      snprintf(FileName, 500, "%s/etc/config.xml", SimbatBase);
      if ( !Cfg.LoadFromFile(FileName) )
      {
         BkPrintError("Load config.xml fail, ErrStr = %s", Cfg.GetErrorString().c_str());
         return 8;
      }
      if ( BackupName == NULL )
      {
         if ( Cfg.GetValue("HOTBACK_ADDR") == "" )
         {
            BkPrintError("HOTBACK_ADDR not found in config.xml");
            return 8;
         }
         Cfg.SetValue("HOTBACK_ENABLE", "true");
         Cfg.SetValue("HOTBACK_PAUSED", "false");
      }
      else
      {
         bool Found = false;
         vector<HXmlTree*> NodeVec;
         Cfg.GetChildren("BACKUP", NodeVec);
         for ( int i = 0; i < NodeVec.size(); i++ )
         {
            HXmlTree *Node = NodeVec[i];
            if ( Node->GetValue("NAME") != BackupName )
            {
               continue;
            }
            Node->SetValue("ENABLE", "true");
            Node->SetValue("PAUSED", "false");
            Found = true;
            break;
         }
         if ( !Found )
         {
            BkPrintError("BACKUP %s not found in config.xml", BackupName);
            return 8;
         }
      }
      Cfg.SaveToFile(FileName);
   }

   return ReloadConfig();
}

static int DisableBackup(const char *BackupName)
{
   snprintf(ErrMsg, 8192, "DisableBackup(%s)", BackupName == NULL ? "" : BackupName);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   {
      char FileName[500 + 1];
      HXmlTree Cfg;
      HFileLockWrite Lock("$SIMBAT_BASE/tmp/config.lck");
      snprintf(FileName, 500, "%s/etc/config.xml", SimbatBase);
      if ( !Cfg.LoadFromFile(FileName) )
      {
         BkPrintError("Load config.xml fail, ErrStr = %s", Cfg.GetErrorString().c_str());
         return 8;
      }
      if ( BackupName == NULL )
      {
         if ( Cfg.GetValue("HOTBACK_ADDR") == "" )
         {
            BkPrintError("HOTBACK_ADDR not found in config.xml");
            return 8;
         }
         Cfg.SetValue("HOTBACK_ENABLE", "false");
      }
      else
      {
         bool Found = false;
         vector<HXmlTree*> NodeVec;
         Cfg.GetChildren("BACKUP", NodeVec);
         for ( int i = 0; i < NodeVec.size(); i++ )
         {
            HXmlTree *Node = NodeVec[i];
            if ( Node->GetValue("NAME") != BackupName )
            {
               continue;
            }
            Node->SetValue("ENABLE", "false");
            Found = true;
            break;
         }
         if ( !Found )
         {
            BkPrintError("BACKUP %s not found in config.xml", BackupName);
            return 8;
         }
      }
      Cfg.SaveToFile(FileName);
   }

   return ReloadConfig();
}

static int Create(const char *InstanceId)
{
   snprintf(ErrMsg, 8192, "Create(%s)", InstanceId);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   char NewBaseDir[500 + 1];

   BkStrNCpy(NewBaseDir, SimbatBase, sizeof(NewBaseDir) - 1);

   int Len = strlen(NewBaseDir);

   for ( ; Len > 0 && NewBaseDir[Len - 1] != '/'; Len-- )
      ;

   if ( Len > 0 )
   {
      NewBaseDir[Len - 1] = '\0';
   }

   Len = strlen(NewBaseDir);
   snprintf(NewBaseDir + Len, 500 - Len, "/%s", InstanceId);

   if ( FileExist(NewBaseDir) )
   {
      BkPrintError("%s exist", NewBaseDir);
      return 8;
   }

   char Command[2048 + 1];

   snprintf(Command, 2048, "mkdir %s", NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "ln -s %s/bin %s/bin", SimbatBase, NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "ln -s %s/include %s/include", SimbatBase, NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "ln -s %s/lib %s/lib", SimbatBase, NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "ln -s %s/plugin %s/plugin", SimbatBase, NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "ln -s %s/sample %s/sample", SimbatBase, NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "mkdir %s/bats %s/etc %s/tmp", NewBaseDir, NewBaseDir, NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "cp %s/etc/config.xml %s/etc", SimbatBase, NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "cp %s/etc/config_cust_default.xml %s/etc", SimbatBase, NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "cp %s/etc/auth_default.xml %s/etc", SimbatBase, NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   snprintf(Command, 2048, "echo '1 master' > %s/etc/my_id", NewBaseDir);
   fprintf(stderr, "### executing %s\n", Command);
   system(Command);
   return 0;
}

static int Encrypt()
{
   char NewPass[200 + 1];
   char NewPass2[200 + 1];
   char TmpStr[200 + 1];

   system("stty -echo");
   fprintf(stderr, "Please enter password: ");
   ReadLine(stdin, NewPass, 200);
   fprintf(stderr, "\nPlease enter password again: ");
   ReadLine(stdin, NewPass2, 200);
   system("stty echo");
   fprintf(stderr, "\n");

   if ( strcmp(NewPass, NewPass2) != 0 )
   {
      fprintf(stderr, "password not match\n");
      return 8;
   }

   NewPass[32] = '\0';
   BkRightTrim(NewPass);

   int Len = strlen(NewPass);

   if ( Len < 32 )
   {
      memset(NewPass + Len, '\0', 32 - Len);
   }

   BkTeaEncrypt(NewPass, PASS_KEY, 32);
   BkHexToAsc(NewPass, TmpStr, 32);
   TmpStr[32 * 2] = '\0';
   printf("%s\n", TmpStr);
   return 0;
}

static int UnloadScript(const char *JobId, const char *ScriptFileName)
{
   HString GroupName;
   HString JobName;
   int PartIdx = 0;
   HString Command;
   HString PartitionFile;

   {
      vector<HString> StrVec;
      SplitString(JobId, JobNameDelimiter[0], StrVec, true);
      if ( StrVec.size() != 2 && StrVec.size() != 3 )
      {
         BkPrintError("Invalid job_id '%s'", JobId);
         return 8;
      }
      GroupName = StrVec[0];
      JobName = StrVec[1];
      if ( StrVec.size() == 3 )
      {
         PartIdx = StrToInt(StrVec[2].c_str());
      }
   }

   {
      char FileName[500 + 1];
      snprintf(FileName, 500, "%s/status/current.sbt", ShrDir.c_str());
      HXmlTree Xml;
      if ( !Xml.LoadFromFile(FileName) )
      {
         BkPrintError("Load current.sbt fail, ErrStr = %s", Xml.GetErrorString().c_str());
         return 8;
      }
      vector<HString> IncludeFiles;
      Xml.GetValues("INCLUDE_FILES", IncludeFiles);
      for ( int i = 0; i < IncludeFiles.size(); i++ )
      {
         char TmpStr[500 + 1];
         snprintf(TmpStr, 500, "%s", IncludeFiles[i].c_str());
         BkRightTrim(TmpStr);
         BkToLower(TmpStr);
         snprintf(FileName, 500, "%s/bats/%s.sbt", SimbatBase, TmpStr);
         HXmlTree TmpXml;
         if( !TmpXml.LoadFromFile(FileName) )
         {
            BkPrintError("Load %s fail, %s", FileName, Xml.GetErrorString().c_str());
            return false;
         }
         vector<HXmlTree*> Groups;
         TmpXml.GetChildren("GROUP", Groups);
         for ( int j = 0; j < Groups.size(); j++ )
         {
            TmpXml.MoveChildTo(Groups[j], &Xml);
         }
      }
      bool Found = false;
      vector<HXmlTree*> GroupVec;
      Xml.GetChildren("GROUP", GroupVec);
      for ( int i = 0; i < GroupVec.size(); i++ )
      {
         HXmlTree *GroupXml = GroupVec[i];
         if ( GroupName != GroupXml->GetPropertyUpper("NAME") )
         {
            continue;
         }
         vector<HXmlTree*> JobVec;
         GroupXml->GetChildren("JOB", JobVec);
         for ( int j = 0; j < JobVec.size(); j++ )
         {
            HXmlTree *JobXml = JobVec[j];
            if ( JobName != JobXml->GetPropertyUpper("NAME") )
            {
               continue;
            }
            Command = JobXml->GetProperty("COMMAND");
            PartitionFile = JobXml->GetProperty("PARTITION_FILE");
            if ( PartitionFile != "" && strchr(PartitionFile.c_str(), '/') == NULL )
            {  
               PartitionFile = ShrDir + "/part/" + PartitionFile;
            }  
            Found = true;
            break;
         }
         break;
      }
      if ( !Found )
      {
         BkPrintError("job '%s' not found", JobId);
         return 8;
      }
   }

   int PartTotal = 0;
   HString PartId;

   if ( PartitionFile != "" )
   {
      char FileName[500 + 1];
      snprintf(FileName, 500, "%s", EnvEvaluate(PartitionFile).c_str());
      FILE *f = fopen(FileName, "rt");
      if ( f == NULL )
      {
         BkPrintError("Can not open %s, errno = %d", FileName, errno);
         return 8;
      }
      for ( int PartTotal = 0; ; )
      {
         char Line[2048 + 1];
         if ( ReadLine(f, Line, 2048) == -1 )
         {
            break;
         }
         if ( Line[0] == '#' )
         {
            continue;
         }
         BkRightTrim(Line);
         PartTotal++;
         if ( PartTotal == PartIdx )
         {
            PartId = Line;
         }
      }
      fclose(f);
   }

   FILE *fout = fopen(ScriptFileName, "wt");

   if ( fout == NULL )
   {
      BkPrintError("Can not create %s, errno = %d", ScriptFileName, errno);
      return 8;
   }

   {
      char FileName[500 + 1];
      snprintf(FileName, 500, "%s/status/var_pool", ShrDir.c_str());
      HXmlTree Xml;
      if ( !Xml.LoadFromFile(FileName) )
      {
         BkPrintError("Load var_pool fail, ErrStr = %s", Xml.GetErrorString().c_str());
         return 8;
      }
      vector<HXmlTree*> Nodes;
      Xml.GetChildren(NULL, Nodes);
      fprintf(fout, "# Vars from var_pool\n");
      for ( int i = 0; i < Nodes.size(); i++ )
      {
         HString Name = Nodes[i]->GetTag();
         HString Value = Nodes[i]->GetData();
         fprintf(fout, "export %s='%s'\n", Name.c_str(), Value.c_str());
      }
      fprintf(fout, "#\n");
      fprintf(fout, "export SIMBAT_RUNNING_TIMES=1\n");
      fprintf(fout, "export SIMBAT_NODE_ID=1\n");
      fprintf(fout, "export SIMBAT_REGION_ID=1\n");
      fprintf(fout, "export SIMBAT_PART_TOTAL=%d\n", PartTotal);
      if ( PartitionFile != "" && PartIdx == 0 )
      {
         fprintf(fout, "while read Line\n");
         fprintf(fout, "do\n");
         fprintf(fout, "   export SIMBAT_PART=\"$Line\"\n");
         fprintf(fout, "   export SIMBAT_JOB_SEQ=`simbat_ctl get_seq`\n");
         fprintf(fout, "   export SIMBAT_JOB_SEQ_DATE=`echo $SIMBAT_JOB_SEQ|cut -c1-8`\n");
         fprintf(fout, "   export SIMBAT_JOB_SEQ_SEQ=`echo $SIMBAT_JOB_SEQ|cut -c9-`\n");
         fprintf(fout, "   %s\n", Command.c_str());
         fprintf(fout, "done < %s\n", PartitionFile.c_str());
      }
      else
      {
         fprintf(fout, "export SIMBAT_PART='%s'\n", PartId.c_str());
         fprintf(fout, "export SIMBAT_JOB_SEQ=`simbat_ctl get_seq`\n");
         fprintf(fout, "export SIMBAT_JOB_SEQ_DATE=`echo $SIMBAT_JOB_SEQ|cut -c1-8`\n");
         fprintf(fout, "export SIMBAT_JOB_SEQ_SEQ=`echo $SIMBAT_JOB_SEQ|cut -c9-`\n");
         fprintf(fout, "%s\n", Command.c_str());
      }
   }

   fclose(fout);
   return 0;
}

static int GetSeq()
{
   HString Result;
   char Buf[500 + 1];

   snprintf(Buf, 500, "o|||");
   CallServer(Syslog, 20000, SelfAddr, "GET_SEQ", "", Buf, false, Result);
   printf("%s\n", Result.c_str());
   return 0;
}

static int Compress(const char *InputFile, const char *OutputFile)
{
   FILE *fin = fopen(InputFile, "rb");

   if ( fin == NULL )
   {
      BkPrintError("Can not open %s, errno = %d", InputFile, errno);
      return 8;
   }

   FILE *fout = fopen(OutputFile, "wb");

   if ( fout == NULL )
   {
      BkPrintError("Can not create %s, errno = %d", OutputFile, errno);
      fclose(fin);
      return 8;
   }

   Lz77CompressFile(fin, fout);
   fclose(fout);
   fclose(fin);
   return 0;
}

static int Decompress(const char *InputFile, const char *OutputFile)
{
   FILE *fin = fopen(InputFile, "rb");

   if ( fin == NULL )
   {
      BkPrintError("Can not open %s, errno = %d", InputFile, errno);
      return 8;
   }

   FILE *fout = fopen(OutputFile, "wb");

   if ( fout == NULL )
   {
      BkPrintError("Can not create %s, errno = %d", OutputFile, errno);
      fclose(fin);
      return 8;
   }

   Lz77DecompressFile(fin, fout);
   fclose(fout);
   fclose(fin);
   return 0;
}

static int StartBatch(const char *Addr, const char *BatchName,
   const char *BatchDate, const char *EndBatchDate, bool NextBatch)
{
   snprintf(ErrMsg, 8192, "StartBatch(Addr = %s, BatchName = %s, BatchDate = %s, EndBatchDate = %s, NextBatch = %d)",
      Addr, BatchName, BatchDate, EndBatchDate, NextBatch);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   vector<HString> AddrVec;

   SplitString(Addr, ',', AddrVec, true);

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      if ( strchr(IpPort.c_str(), ':') == NULL )
      {
         char TmpStr[500 + 1];
         snprintf(TmpStr, 500, "%s:%d", IpPort.c_str(), LocalPort);
         IpPort = TmpStr;
      }
   }

   HString Result;

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      char Buf[500 + 1];
      snprintf(Buf, 500, "%c|||%s|%s|%s", NextBatch ? 'I' : '1', BatchName, BatchDate, EndBatchDate);
      if ( CallServer(Syslog, 60000, IpPort, NextBatch ? "NEXT_BATCH" : "START_BATCH", BatchName, Buf, true, Result) )
      {  
         return 0;
      }
   }

   BkPrintError("%s fail, %s", NextBatch ? "Next batch" : "Start batch", Result.c_str());
   return 8;
}

static int SetParaJobNum(const char *Addr, const char *ParaJobNumStr)
{
   snprintf(ErrMsg, 8192, "SetParaJobNum(Addr = %s, ParaJobNumStr = %s)", Addr, ParaJobNumStr);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   int ParaJobNum = StrToInt(ParaJobNumStr);

   if ( ParaJobNum <= 0 || ParaJobNum > 10000 )
   {
      snprintf(ErrMsg, 8192, "Invalid para_job_num(1 - 10000)");
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      BkPrintError("%s", ErrMsg);
      return 8;
   }

   vector<HString> AddrVec;

   SplitString(Addr, ',', AddrVec, true);

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      if ( strchr(IpPort.c_str(), ':') == NULL )
      {
         char TmpStr[500 + 1];
         snprintf(TmpStr, 500, "%s:%d", IpPort.c_str(), LocalPort);
         IpPort = TmpStr;
      }
   }

   HString Result;

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      char Buf[500 + 1];
      snprintf(Buf, 500, "%c|||%s", 'J', ParaJobNumStr);
      if ( CallServer(Syslog, 60000, IpPort, "SET_PARA_JOB_NUM", ParaJobNumStr, Buf, true, Result) )
      {
         return 0;
      }
   }

   BkPrintError("Set para job num fail, %s", Result.c_str());
   return 8;
}

static int Skip(const char *Addr, const char *JobId)
{
   snprintf(ErrMsg, 8192, "Skip(Addr = %s, JobId = %s)", Addr, JobId);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   vector<HString> AddrVec;

   SplitString(Addr, ',', AddrVec, true);

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      if ( strchr(IpPort.c_str(), ':') == NULL )
      {
         char TmpStr[500 + 1];
         snprintf(TmpStr, 500, "%s:%d", IpPort.c_str(), LocalPort);
         IpPort = TmpStr;
      }
   }

   HString Result;

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      char Buf[500 + 1];
      snprintf(Buf, 500, "5|||%s", JobId);
      if ( CallServer(Syslog, 60000, IpPort, "SKIP_JOB", JobId, Buf, true, Result) )
      {
         return 0;
      }
   }

   BkPrintError("Skip job fail, %s", Result.c_str());
   return 8;
}

static int Rerun(const char *Addr, const char *JobId)
{
   snprintf(ErrMsg, 8192, "Rerun(Addr = %s, JobId = %s)", Addr, JobId);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   vector<HString> AddrVec;

   SplitString(Addr, ',', AddrVec, true);

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      if ( strchr(IpPort.c_str(), ':') == NULL )
      {
         char TmpStr[500 + 1];
         snprintf(TmpStr, 500, "%s:%d", IpPort.c_str(), LocalPort);
         IpPort = TmpStr;
      }
   }

   HString Result;

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      char Buf[500 + 1];
      snprintf(Buf, 500, "6|||%s", JobId);
      if ( CallServer(Syslog, 60000, IpPort, "RERUN_JOB", JobId, Buf, true, Result) )
      {
         return 0;
      }  
   }

   BkPrintError("Rerun job fail, %s", Result.c_str());
   return 8;
}

static int Restore(const char *Addr, const char *JobId)
{
   snprintf(ErrMsg, 8192, "Restore(Addr = %s, JobId = %s)", Addr, JobId);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   vector<HString> AddrVec;

   SplitString(Addr, ',', AddrVec, true);

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      if ( strchr(IpPort.c_str(), ':') == NULL )
      {
         char TmpStr[500 + 1];
         snprintf(TmpStr, 500, "%s:%d", IpPort.c_str(), LocalPort);
         IpPort = TmpStr;
      }
   }

   HString Result;

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      char Buf[500 + 1];
      snprintf(Buf, 500, "D|||%s", JobId);
      if ( CallServer(Syslog, 60000, IpPort, "RESTORE_JOB", JobId, Buf, true, Result) )
      {
         return 0;
      }
   }

   BkPrintError("Restore job fail, %s", Result.c_str());
   return 8;
}

void PrintCopyright()
{
   fprintf(stderr, "\nSIM-BAT version %s  ( %s )\n", VERSION_STRING, EDITION_STRING);
   fprintf(stderr, "Build time : %s, %s\n", __DATE__, __TIME__);

   const char *Base = getenv("SIMBAT_BASE");

   if ( Base == NULL )
   {
      fprintf(stderr, "SIMBAT_BASE not set\n");
   }
   else
   {
      fprintf(stderr, "SIMBAT_BASE=%s\n", Base);
   }

   if ( Copyright )
   {
      fprintf(stderr, "\nCopyright(C) 2013, 2018, "
         "ShenZhen Simjie Information Technology Ltd.\n");
      fprintf(stderr, "All right reserved.\n\n");
   }
}

void PrintUsage()
{
   PrintCopyright();
   fprintf(stderr, "Usage : [PATH/]simbat_ctl start|stop|status\n");
   fprintf(stderr, "        [PATH/]simbat_ctl [--addr=host_name:port[,host_name2:port2]]\n"
                   "                          [--date=batch_date] [--end_date=end_batch_date] start_batch batch_name\n");
   fprintf(stderr, "        [PATH/]simbat_ctl [--addr=host_name:port[,host_name2:port2]] skip job_id\n");
   fprintf(stderr, "        [PATH/]simbat_ctl [--addr=host_name:port[,host_name2:port2]] rerun job_id\n");
   fprintf(stderr, "        [PATH/]simbat_ctl [--addr=host_name:port[,host_name2:port2]] restore job_id\n");
   fprintf(stderr, "        [PATH/]simbat_ctl [--addr=host_name:port[,host_name2:port2]] next_batch batch_name\n");
   fprintf(stderr, "        [PATH/]simbat_ctl [--addr=host_name:port[,host_name2:port2]] set_para_job_num para_job_num\n");
   fprintf(stderr, "        [PATH/]simbat_ctl dump\n");
   fprintf(stderr, "        [PATH/]simbat_ctl reload_config\n");
   fprintf(stderr, "        [PATH/]simbat_ctl switch_to master|backup\n");
   fprintf(stderr, "        [PATH/]simbat_ctl enable_backup [backup_name]\n");
   fprintf(stderr, "        [PATH/]simbat_ctl disable_backup [backup_name]\n");
   fprintf(stderr, "        [PATH/]simbat_ctl create instance_id\n");
   fprintf(stderr, "        [PATH/]simbat_ctl encrypt\n");
   fprintf(stderr, "        [PATH/]simbat_ctl unload_script job_id script_file\n");
   fprintf(stderr, "        [PATH/]simbat_ctl get_seq\n");
   fprintf(stderr, "        [PATH/]simbat_ctl compress input_file output_file\n");
   fprintf(stderr, "        [PATH/]simbat_ctl decompress input_file output_file\n");

}

int main(int argc, char *argv[])
{
   char *s;
   char FileName[500 + 1];

   if ( strchr(argv[0], '/') != NULL )
   {
      SetBaseByPath(argv[0]);
   }

   s = getenv("SIMBAT_BASE");

   if ( s == NULL )
   {
      BkPrintError("$SIMBAT_BASE not defined");
      return 8;
   }

   SimbatBase = s;

   {
      snprintf(FileName, 500, "%s/etc/install.cfg", SimbatBase);
      FILE *f = fopen(FileName, "rt");
      if ( f != NULL )
      {
         for ( ; ; )
         {
            char Line[2048 + 1];
            if ( ReadLine(f, Line, 2048) == -1 )
            {
               break;
            }
            BkRightTrim(Line);
            if ( strcmp(Line, "copyright=true") == 0 )
            {
               Copyright = true;
               break;
            }
         }
         fclose(f);
      }
   }

   snprintf(BkTmpDir, 500, "%s/tmp", SimbatBase);

   if ( argc == 1 )
   {
      PrintUsage();
      return 8;
   }

   HString ErrStr;
   const char *Addr = "127.0.0.1";
   const char *BatchDate = "";
   const char *EndBatchDate = "";

   if ( !LoadCfg(ErrStr) )
   {
      BkPrintError("Load config.xml fail, %s", ErrStr.c_str());
      PrintUsage();
      return 8;
   }

   int Rc;
   bool StatusFlag = false;
   int i = 1;

   for ( ; i < argc; i++ )
   {
      if ( memcmp(argv[i], "--addr=", 7) == 0 )
      {
         Addr = argv[i] + 7;
         continue;
      }
      if ( memcmp(argv[i], "--date=", 7) == 0 )
      {
         BatchDate = argv[i] + 7;
         continue;
      }
      if ( memcmp(argv[i], "--end_date=", 11) == 0 )
      {
         EndBatchDate = argv[i] + 11;
         continue;
      }
      if ( argv[i][0] == '-' )
      {
         PrintUsage();
         return 8;
      }
      else
      {
         break;
      }
   }

   if ( strcmp(argv[i], "start") == 0 )
   {
      PrintCopyright();
      Rc = Start();
   }
   else if ( strcmp(argv[i], "stop") == 0 )
   {
      Rc = Stop();
   }
   else if ( strcmp(argv[i], "dump") == 0 )
   {
      Rc = Dump();
   }
   else if ( strcmp(argv[i], "reload_config") == 0 )
   {
      Rc = ReloadConfig();
   }
   else if ( strcmp(argv[i], "switch_to") == 0 )
   {
      if ( argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      Rc = SwitchTo(argv[i + 1]);
   }
   else if ( strcmp(argv[i], "enable_backup") == 0 )
   {
      if ( argc != i + 1 && argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      if ( argc == i + 1 )
      {
         Rc = EnableBackup(NULL);
      }
      else
      {
         Rc = EnableBackup(argv[i + 1]);
      }
   }
   else if ( strcmp(argv[i], "disable_backup") == 0 )
   {
      if ( argc != i + 1 && argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      if ( argc == i + 1 )
      {
         Rc = DisableBackup(NULL);
      }
      else
      {
         Rc = DisableBackup(argv[i + 1]);
      }
   }
   else if ( strcmp(argv[i], "create") == 0 )
   {
      if ( argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      Rc = Create(argv[i + 1]);
   }
   else if ( strcmp(argv[i], "encrypt") == 0 )
   {
      Rc = Encrypt();
   }
   else if ( strcmp(argv[i], "unload_script") == 0 )
   {
      if ( argc != i + 3 )
      {
         PrintUsage();
         return 8;
      }
      Rc = UnloadScript(argv[i + 1], argv[i + 2]);
   }
   else if ( strcmp(argv[i], "get_seq") == 0 )
   {
      Rc = GetSeq();
   }
   else if ( strcmp(argv[i], "compress") == 0 )
   {
      if ( argc != i + 3 )
      {
         PrintUsage();
         return 8;
      }
      Rc = Compress(argv[i + 1], argv[i + 2]);
   }
   else if ( strcmp(argv[i], "decompress") == 0 )
   {
      if ( argc != i + 3 )
      {
         PrintUsage();
         return 8;
      }
      Rc = Decompress(argv[i + 1], argv[i + 2]);
   }
   else if ( strcmp(argv[i], "status") == 0 )
   {
      PrintCopyright();
      StatusFlag = true;
      Rc = Status();
   }
   else if ( strcmp(argv[i], "start_batch") == 0 )
   {
      if ( argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      Rc = StartBatch(Addr, argv[i + 1], BatchDate, EndBatchDate, false);
   }
   else if ( strcmp(argv[i], "skip") == 0 )
   {
      if ( argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      Rc = Skip(Addr, argv[i + 1]);
   }
   else if ( strcmp(argv[i], "rerun") == 0 )
   {
      if ( argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      Rc = Rerun(Addr, argv[i + 1]);
   }
   else if ( strcmp(argv[i], "restore") == 0 )
   {
      if ( argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      Rc = Restore(Addr, argv[i + 1]);
   }
   else if ( strcmp(argv[i], "next_batch") == 0 )
   {
      if ( argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      Rc = StartBatch(Addr, argv[i + 1], BatchDate, "", true);
   }
   else if ( strcmp(argv[i], "set_para_job_num") == 0 )
   {
      if ( argc != i + 2 )
      {
         PrintUsage();
         return 8;
      }
      Rc = SetParaJobNum(Addr, argv[i + 1]);
   }
   else
   {
      PrintUsage();
      return 8;
   }

   snprintf(ErrMsg, 8192, "Process completed, rc = %d", Rc);
   Syslog.Log(__FILE__, __LINE__, StatusFlag ? llDebug : llInfo, "EXIT", ErrMsg);
   return Rc;
}
